package com.meida.bpm.provider.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.meida.bpm.client.entity.FlowProcess;
import com.meida.bpm.provider.common.cmd.ProcessModelDeployCmd;
import com.meida.bpm.provider.common.cmd.ProcessModelSaveCmd;
import com.meida.bpm.provider.mapper.FlowProcessMapper;
import com.meida.bpm.provider.service.FlowProcessService;
import com.meida.bpm.provider.service.ProcessDefinitionService;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.mybatis.query.CriteriaDelete;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.mybatis.query.CriteriaSave;
import com.meida.common.mybatis.query.CriteriaUpdate;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.ApiAssert;
import com.meida.common.base.utils.FlymeUtils;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.persistence.entity.DeploymentEntityImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 流程表接口实现类
 *
 * @author flyme
 * @date 2020-06-11
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class FlowProcessServiceImpl extends BaseServiceImpl<FlowProcessMapper, FlowProcess> implements FlowProcessService {

    @Autowired
    protected RepositoryService repositoryService;

    @Autowired
    protected ManagementService managementService;

    @Autowired
    protected ProcessDefinitionService processDefinitionService;

    @Override
    public ResultBody basePageList(CriteriaQuery<?> cq) {
        cq.eq(FlowProcess.class, "companyId", OpenHelper.getCompanyId());
        return super.basePageList(cq);
    }

    @Override
    public void afterPageList(CriteriaQuery<FlowProcess> cq, List<EntityMap> data, ResultBody resultBody) {
        for (EntityMap datum : data) {
            String procDefId = datum.get("procDefId");
            if (FlymeUtils.isNotEmpty(procDefId)) {
                ProcessDefinition processDefinition = processDefinitionService.getProcessDefinitionById(procDefId);
                datum.put("suspended", processDefinition.isSuspended());
            }
        }
        super.afterPageList(cq, data, resultBody);
    }

    @Override
    public ResultBody beforeAdd(CriteriaSave cs, FlowProcess process, EntityMap extra) {
        process.setCompanyId(OpenHelper.getCompanyId());
        Model model = saveModel(process);
        process.setProcModelId(model.getId());
        return ResultBody.ok();
    }

    private Model saveModel(FlowProcess flowProcess) {
        String processKey = flowProcess.getProcessKey();
        checkModelKeyExists(processKey);
        Model model = repositoryService.newModel();
        model.setKey(flowProcess.getProcessKey());
        model.setName(flowProcess.getProcessName());
        model.setVersion(1);
        model.setTenantId(flowProcess.getCompanyId().toString());
        model.setMetaInfo(getMetaInfo(flowProcess));
        repositoryService.saveModel(model);
        //保存流程文件
        managementService.executeCommand(new ProcessModelSaveCmd(model.getId(), flowProcess.getProcessXml()));
        return model;

    }

    @Override
    public ResultBody afterAdd(CriteriaSave cs, FlowProcess flowProcess, EntityMap extra) {
        return super.afterAdd(cs, flowProcess, extra);
    }


    @Override
    public ResultBody afterEdit(CriteriaUpdate cu, FlowProcess flowProcess, EntityMap extra) {
        Model model = getModelById(flowProcess.getProcModelId());
        String processXml = flowProcess.getProcessXml();
        model.setKey(flowProcess.getProcessKey());
        model.setName(flowProcess.getProcessName());
        model.setTenantId(OpenHelper.getCompanyId().toString());
        model.setMetaInfo(getMetaInfo(flowProcess));
        repositoryService.saveModel(model);
        if (FlymeUtils.isNotEmpty(processXml)) {
            //保存流程文件
            managementService.executeCommand(new ProcessModelSaveCmd(model.getId(), processXml));
        }
        return super.afterEdit(cu, flowProcess, extra);
    }

    protected Model getModelById(String modelId) {
        Model model = repositoryService.getModel(modelId);
        ApiAssert.isNotEmpty("流程模型不存在", model);
        return model;
    }

    private String getMetaInfo(FlowProcess flowProcess) {
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode metaInfo = objectMapper.createObjectNode();
        metaInfo.put("name", flowProcess.getProcessName());
        metaInfo.put("description", flowProcess.getDescription());
        return metaInfo.toString();
    }


    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public ResultBody beforePageList(CriteriaQuery<FlowProcess> cq, FlowProcess process, EntityMap requestMap) {
        cq.orderByDesc("process.createTime");
        return ResultBody.ok();
    }

    @Override
    public ResultBody deleteByProcDefId(String procDefId) {
        CriteriaDelete cd = new CriteriaDelete();
        cd.eq(true, "procDefId", procDefId);
        Boolean tag = remove(cd);
        return ResultBody.ok(tag);
    }

    @Override
    public ResultBody deployModel(String procModelId, String processId) {
        DeploymentEntityImpl obj = (DeploymentEntityImpl) managementService.executeCommand(new ProcessModelDeployCmd(procModelId));
        List<ProcessDefinitionEntityImpl> processDefList = obj.getDeployedArtifacts(ProcessDefinitionEntityImpl.class);
        if (FlymeUtils.isNotEmpty(processDefList)) {
            FlowProcess flowProcess = getById(processId);
            for (ProcessDefinitionEntityImpl entity : processDefList) {
                //关联流程定义id
                flowProcess.setProcDefId(entity.getId());
                updateById(flowProcess);
            }
        }
        return ResultBody.ok();
    }

    @Override
    public FlowProcess getByProcessKey(String processKey) {
        CriteriaQuery cq = new CriteriaQuery(FlowProcess.class);
        cq.eq(true, "processKey", processKey);
        return getOne(cq);
    }


    @Override
    public void addProcess(DeploymentEntityImpl deploymentEntity, String processXml) {
        List<ProcessDefinitionEntityImpl> process = deploymentEntity.getDeployedArtifacts(ProcessDefinitionEntityImpl.class);
        if (FlymeUtils.isNotEmpty(process)) {
            for (ProcessDefinitionEntityImpl entity : process) {
                FlowProcess flowProcess = new FlowProcess();
                flowProcess.setProcessName(entity.getName());
                flowProcess.setProcessKey(entity.getKey());
                flowProcess.setProcessXml(processXml);
                flowProcess.setProcessState(entity.getSuspensionState());
                flowProcess.setProcDefId(entity.getId());
                flowProcess.setDescription(entity.getDescription());
                flowProcess.setVersionTag(entity.getVersion());
                save(flowProcess);
            }
        }
    }

    @Override
    public void importProcess(DeploymentEntityImpl deploymentEntity, String processXml) {
        List<ProcessDefinitionEntityImpl> process = deploymentEntity.getDeployedArtifacts(ProcessDefinitionEntityImpl.class);
        if (FlymeUtils.isNotEmpty(process)) {
            for (ProcessDefinitionEntityImpl entity : process) {
                FlowProcess flowProcess = new FlowProcess();
                flowProcess.setProcessName(entity.getName());
                flowProcess.setProcessKey(entity.getKey());
                flowProcess.setProcessXml(processXml);
                flowProcess.setProcessState(entity.getSuspensionState());
                flowProcess.setDescription(entity.getDescription());
                flowProcess.setVersionTag(entity.getVersion());
                flowProcess.setCompanyId(OpenHelper.getCompanyId());
                //保存流程模型
                Model model = saveModel(flowProcess);
                flowProcess.setProcModelId(model.getId());
                save(flowProcess);
                //删除流程定义
                processDefinitionService.delete(entity.getId());

            }
        }
    }

    protected void checkModelKeyExists(String modelKey) {
        long countNum = repositoryService.createModelQuery().modelKey(modelKey).count();
        if (countNum > 0) {
            throw new FlowableObjectNotFoundException("ModelKey already exists with id " + modelKey);
        }
    }
}
